﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Z1029.NoteGlobalView
{
    public class ChartReader
    {
        public static Chart ReadChart(string file)
        {
            return JsonUtility.FromJson<Chart>(file);
        }
    }

    [Serializable]
    public class Chart
    {
        public string chartVersion;
        public float offset;
        public int numOfNotes;
        public JudgeLineSet[] judgeLineList;
    }

    [Serializable]
    public struct SpeedEvent : IComparable<SpeedEvent>
    {
        public float startTime;
        public float endTime;
        public float value;

        public int CompareTo(SpeedEvent other)
        {
            if (startTime > other.startTime)
            {
                return 1;
            }
            else if (startTime < other.startTime)
            {
                return -1;
            }
            else return 0;
        }
    }

    [Serializable]
    public class JudgeLineSet
    {
        public float bpm;
        public SpeedEvent[] speedEvents;
        public Note.NoteData[] notesAbove;
        public Note.NoteData[] notesBelow;
        public List<ValueSet> alphaEvents;
        public List<ValueSet> moveEvents;
        public List<ValueSet> rotateEvents;
    }

    [Serializable]
    public class ValueSet
    {
        public float startTime;
        public float endTime;
        public float start;
        public float end;
        public float start2;
        public float end2;
    }

    public class PhigrosChartReader : ChartReader
    {
        public static Chart ReadChart(string file)
        {
            Chart chart = JsonUtility.FromJson<Chart>(file);
            PhigrosChart gameChart = JsonUtility.FromJson<PhigrosChart>(file);
            int ver = gameChart.formatVersion;

            int notesCount = 0;
            for (int i = 0; i < gameChart.judgeLineList.Count; i++)
            {
                for (int j = 0; j < gameChart.judgeLineList[i].notesAbove.Count; j++)
                {
                    Note.NoteData eve = default;
                    int type = gameChart.judgeLineList[i].notesAbove[j].type;
                    switch (type)
                    {
                        case 1:
                            eve.type = NoteType.Click;
                            break;
                        case 4://4
                            eve.type = NoteType.Flick;
                            break;
                        case 3://3
                            eve.type = NoteType.Hold;
                            break;
                        case 2://2
                            eve.type = NoteType.Drag;
                            break;
                    }
                    eve.beatTime = gameChart.judgeLineList[i].notesAbove[j].time;
                    eve.time = gameChart.judgeLineList[i].notesAbove[j].time * (60 / gameChart.judgeLineList[i].bpm) / 32;
                    eve.positionX = gameChart.judgeLineList[i].notesAbove[j].positionX;
                    //eve.speed = gameChart.judgeLineList[i].notesAbove[j].speed * (gameChart.formatVersion == 1 ? gameChart.judgeLineList[i].bpm / 16 : gameChart.judgeLineList[i].bpm / 16);
                    eve.speed = gameChart.judgeLineList[i].notesAbove[j].speed * 12;
                    if (type == 3) eve.speed /= GetJudgelineSpeed((eve.beatTime + gameChart.judgeLineList[i].notesAbove[j].holdTime / 2), gameChart.judgeLineList[i].speedEvents.ToArray()) == 0 ? 1 : GetJudgelineSpeed((eve.beatTime + gameChart.judgeLineList[i].notesAbove[j].holdTime / 2), gameChart.judgeLineList[i].speedEvents.ToArray());
                    eve.beatHoldTime = (int)gameChart.judgeLineList[i].notesAbove[j].holdTime;
                    eve.index = notesCount;
                    chart.judgeLineList[i].notesAbove[j] = eve;
                    //notesManager.InstantiateNote(gameChart.judgeLineList[i].notesAbove[j].type, eve, judgelines[i], Note.Direction.Above);
                    notesCount++;
                }
                for (int k = 0; k < gameChart.judgeLineList[i].notesBelow.Count; k++)
                {
                    Note.NoteData eve2 = default;
                    int type = gameChart.judgeLineList[i].notesBelow[k].type;
                    switch (type)
                    {
                        case 1:
                            eve2.type = NoteType.Click;
                            break;
                        case 4://4
                            eve2.type = NoteType.Flick;
                            break;
                        case 3://3
                            eve2.type = NoteType.Hold;
                            break;
                        case 2://2
                            eve2.type = NoteType.Drag;
                            break;
                    }
                    eve2.beatTime = gameChart.judgeLineList[i].notesBelow[k].time;
                    eve2.time = gameChart.judgeLineList[i].notesBelow[k].time * (60 / gameChart.judgeLineList[i].bpm) / 32;
                    eve2.positionX = gameChart.judgeLineList[i].notesBelow[k].positionX;
                    //eve2.speed = gameChart.judgeLineList[i].notesBelow[k].speed * (gameChart.formatVersion == 1 ? gameChart.judgeLineList[i].bpm / 16 : gameChart.judgeLineList[i].bpm / 16);
                    eve2.speed = gameChart.judgeLineList[i].notesBelow[k].speed * 12;
                    if (type == 3) eve2.speed /= GetJudgelineSpeed((eve2.beatTime + gameChart.judgeLineList[i].notesBelow[k].holdTime / 2), gameChart.judgeLineList[i].speedEvents.ToArray()) == 0 ? 1 : GetJudgelineSpeed((eve2.beatTime + gameChart.judgeLineList[i].notesBelow[k].holdTime / 2), gameChart.judgeLineList[i].speedEvents.ToArray());
                    eve2.beatHoldTime = (int)gameChart.judgeLineList[i].notesBelow[k].holdTime;
                    eve2.index = notesCount;
                    chart.judgeLineList[i].notesBelow[k] = eve2;
                    //notesManager.InstantiateNote(gameChart.judgeLineList[i].notesBelow[k].type, eve2, judgelines[i], Note.Direction.Below);
                    notesCount++;
                }
            }

            for (int i = 0; i < gameChart.judgeLineList.Count; i++)
            {
                chart.judgeLineList[i].bpm = gameChart.judgeLineList[i].bpm;
                chart.judgeLineList[i].speedEvents = new SpeedEvent[gameChart.judgeLineList[i].speedEvents.Count];
                chart.judgeLineList[i].moveEvents = new List<ValueSet>();
                chart.judgeLineList[i].rotateEvents = new List<ValueSet>();
                chart.judgeLineList[i].alphaEvents = new List<ValueSet>();
                List < ValueSet> moveSets = chart.judgeLineList[i].moveEvents;
                SpeedEvent[] speedSets = chart.judgeLineList[i].speedEvents;
                List<ValueSet> rotateSets = chart.judgeLineList[i].rotateEvents;
                List<ValueSet> alphaSets = chart.judgeLineList[i].alphaEvents;

                for (int j = 0; j < chart.judgeLineList[i].speedEvents.Length; j++)
                {
                    float start = gameChart.judgeLineList[i].speedEvents[j].startTime;
                    float end = gameChart.judgeLineList[i].speedEvents[j].endTime;
                    float bpm = gameChart.judgeLineList[i].bpm;
                    speedSets[j].startTime = (float)Utils.GetTimeFromBeatTime(start, bpm);
                    speedSets[j].endTime = (float)Utils.GetTimeFromBeatTime(end, bpm);
                    //if (gameChart.formatVersion == 1) speedSets[j].value = line.speeds[j].value * 10;
                    speedSets[j].value = gameChart.judgeLineList[i].speedEvents[j].value;
                    //line.speeds[j].startTime = line.speeds[j].startTime * (60 / gameChart.judgeLineList[i].bpm) / 32;
                }
                chart.judgeLineList[i].speedEvents = speedSets;

                for (int j = 0; j < gameChart.judgeLineList[i].judgeLineMoveEvents.Count; j++)
                {
                    ValueSet value = gameChart.judgeLineList[i].judgeLineMoveEvents[j];
                    float bpm = gameChart.judgeLineList[i].bpm;
                    float startT = gameChart.judgeLineList[i].judgeLineMoveEvents[j].startTime;
                    float endT = gameChart.judgeLineList[i].judgeLineMoveEvents[j].endTime;
                    float start = gameChart.judgeLineList[i].judgeLineMoveEvents[j].start;
                    float end = gameChart.judgeLineList[i].judgeLineMoveEvents[j].end;
                    float start2 = gameChart.judgeLineList[i].judgeLineMoveEvents[j].start2;
                    float end2 = gameChart.judgeLineList[i].judgeLineMoveEvents[j].end2;
                    value.startTime = (float)Utils.GetTimeFromBeatTime(startT, bpm);
                    value.endTime = (float)Utils.GetTimeFromBeatTime(endT, bpm);
                    if (ver == 3)
                    {
                        value.start = (start - 0.5f) * 18;
                        value.end = (end - 0.5f) * 18;
                        value.start2 = (start2 - 0.5f) * 10;
                        value.end2 = (end2 - 0.5f) * 10;
                    }
                    else if (ver == 1)
                    {

                        float num = gameChart.judgeLineList[i].judgeLineMoveEvents[j].startTime * (60 / gameChart.judgeLineList[i].bpm) / 32;
                        float num2 = gameChart.judgeLineList[i].judgeLineMoveEvents[j].endTime * (60 / gameChart.judgeLineList[i].bpm) / 32;
                        float num3 = gameChart.judgeLineList[i].judgeLineMoveEvents[j].start;
                        float num4 = gameChart.judgeLineList[i].judgeLineMoveEvents[j].end;
                        value.start = Mathf.FloorToInt(num3 / 1000) - 440;
                        value.end = Mathf.FloorToInt(num4 / 1000) - 440;
                        value.start2 = num3 - Mathf.FloorToInt(num3 / 1000) * 1000 - 260;
                        value.end2 = num4 - Mathf.FloorToInt(num4 / 1000) * 1000 - 260;
                        value.start /= 55;
                        value.end /= 55;
                        value.start2 /= 52;
                        value.end2 /= 52;
                    }
                    moveSets.Add(value);
                }
                chart.judgeLineList[i].moveEvents = moveSets;

                for (int j = 0; j < gameChart.judgeLineList[i].judgeLineRotateEvents.Count; j++)
                {
                    ValueSet value = gameChart.judgeLineList[i].judgeLineRotateEvents[j];
                    float bpm = gameChart.judgeLineList[i].bpm;
                    float startT = gameChart.judgeLineList[i].judgeLineRotateEvents[j].startTime;
                    float endT = gameChart.judgeLineList[i].judgeLineRotateEvents[j].endTime;
                    float start = gameChart.judgeLineList[i].judgeLineRotateEvents[j].start;
                    float end = gameChart.judgeLineList[i].judgeLineRotateEvents[j].end;
                    float start2 = gameChart.judgeLineList[i].judgeLineRotateEvents[j].start2;
                    float end2 = gameChart.judgeLineList[i].judgeLineRotateEvents[j].end2;
                    value.startTime = (float)Utils.GetTimeFromBeatTime(startT, bpm);
                    value.endTime = (float)Utils.GetTimeFromBeatTime(endT, bpm);
                    rotateSets.Add(value);
                }
                chart.judgeLineList[i].rotateEvents = rotateSets;

                for (int j = 0; j < gameChart.judgeLineList[i].judgeLineDisappearEvents.Count; j++)
                {
                    ValueSet value = gameChart.judgeLineList[i].judgeLineDisappearEvents[j];
                    float start = gameChart.judgeLineList[i].judgeLineDisappearEvents[j].startTime;
                    float end = gameChart.judgeLineList[i].judgeLineDisappearEvents[j].endTime;
                    float bpm = gameChart.judgeLineList[i].bpm;
                    value.startTime = (float)Utils.GetTimeFromBeatTime(start, bpm);
                    value.endTime = (float)Utils.GetTimeFromBeatTime(end, bpm);
                    alphaSets.Add(value);
                    //line.speeds[j].startTime = line.speeds[j].startTime * (60 / gameChart.judgeLineList[i].bpm) / 32;
                }
                chart.judgeLineList[i].alphaEvents = alphaSets;
            }

            return chart;
        }

        public static float GetJudgelineSpeed(float time, SpeedEvent[] sp)
        {
            float result = 0;
            for (int i = 0; i < sp.Length; i++)
            {
                SpeedEvent sppp = sp[i];
                if (sppp.startTime < time && sppp.endTime > time) result = sppp.value;
            }
            return result;
        }

        [Serializable]
        public class PhigrosChart
        {
            public int formatVersion;
            public float offset;
            public int numOfNotes;
            public List<JudgeLineSet> judgeLineList;
        }

        [Serializable]
        public class JudgeLineSet
        {
            public int numOfNotes;
            public int numOfNotesAbove;
            public int numOfNotesBelow;
            public float bpm;
            public List<SpeedEvent> speedEvents;
            public List<NoteSet> notesAbove;
            public List<NoteSet> notesBelow;
            public List<ValueSet> judgeLineDisappearEvents;
            public List<ValueSet> judgeLineMoveEvents;
            public List<ValueSet> judgeLineRotateEvents;
        }

        [Serializable]
        public class NoteSet
        {
            public int type;
            public int time;
            public float positionX;
            public float holdTime;
            public float speed;
            public float floorPosition;
            public bool isJudged;
            public bool isJudgedForFlick;
            public float realTime;
            public int judgeLineIndex;
            public int noteIndex;
        }
    }

}